package com.apobates.forum.grief.lang;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.safety.Safelist;
import org.jsoup.safety.Whitelist;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 字符串工具类
 *
 * @author xiaofanku
 * @since 20210913
 */
public final class StringUtils {
    public final static String UNIX_LINE = "\n";
    public final static String WIN_LINE = "\r\n";
    private final static Pattern MAIL_REG = Pattern.compile("^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$");
    private final static Logger logger = LoggerFactory.getLogger(StringUtils.class);

    private StringUtils() throws Exception {
        throw new Exception("不需要实例工具类");
    }

    /**
     * 是否是可以使用的字符串
     * 使用lang3.StringUtils.isNotBlank
     *
     * @param str 字符串
     * @return 可用返回true
     */
    public static boolean isNotBlank(String str){
        return org.apache.commons.lang3.StringUtils.isNotBlank(str) && !"null".equalsIgnoreCase(str);
    }

    /**
     * 返回指定长度的随机串(字母和数字组合)
     *
     * @param length 长度
     * @return
     */
    public static String random(int length){
        return org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(length);
    }

    /**
     * 返回在指定范围内随机长度的随机串(字母和数字组合)
     *
     * @param minLength 最小长度
     * @param maxLength 最大长度
     * @return
     */
    public static String random(int minLength, int maxLength){
        return org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(minLength, maxLength);
    }

    /**
     * 返回指定长度的随机串(数字)
     *
     * @param length 长度
     * @return
     */
    public static String randomNumeric(int length){
        return org.apache.commons.lang3.RandomStringUtils.randomNumeric(length);
    }

    /**
     * 返回在指定范围内随机长度的随机串(数字)
     *
     * @param minLength 最小长度
     * @param maxLength 最大长度
     * @return
     */
    public static String randomNumeric(int minLength, int maxLength){
        return org.apache.commons.lang3.RandomStringUtils.randomNumeric(minLength, maxLength);
    }

    /**
     * 清除参数的中非字母符号(a-zA-Z).只适用于英文
     *
     * @param str 字符串源
     * @return
     */
    public static String clearNotAlpha(String str){
        Objects.requireNonNull(str);
        StringBuffer sb = new StringBuffer();
        for(char c : str.toCharArray()){
            if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')){
                sb.append(c);
            }else{
                break;
            }
        }
        return sb.toString();
    }

    /**
     * 只保留参数中的字母和数字,其它的都删除
     *
     * @param str 字符串源
     * @return 如果参数等于null或空字符返回空字符串
     */
    public static String clearNotAlphaNumber(String str) {
        if (isNotBlank(str)) {
            return str.replaceAll("[^a-zA-Z0-9]", "");
        }
        return "";
    }

    /**
     * 中文占2个,大写,小写,空格和数字占1个
     *
     * @param str null输出0
     * @return
     */
    public static int chineseStringLength(String str) {
        if (null == str) {
            return 0;
        }
        char[] c = str.toCharArray();
        int len = 0;
        for (int i = 0; i < c.length; i++) {
            len++;
            if (!isLetter(c[i])) {
                len++;
            }
        }
        return len;
    }

    private static boolean isLetter(char c) {
        int k = 0x80;
        return c / k == 0;
    }

    /**
     * 指定的字符串中包含的子字符的数量.只适用于英文
     *
     * @param source 字符串源
     * @param subStr 待查的子字符串
     * @return
     */
    public static long seek(String source, final String subStr){
        return -1;
    }

    /**
     * 指定的字符串中包含的字符数量.只适用于英文
     *
     * @param source 字符串源
     * @param subChar 待查的字符
     * @return
     */
    public static long seek(String source, final char subChar){
        Objects.requireNonNull(source);
        return source.chars().filter(ch -> ch == subChar).count();
    }

    /**
     * 统计参数中的词频.只适用于英文
     *
     * @param sourceStr 字符串源
     * @return key 词串, Val 词频
     */
    public static Map<String,Integer> wordFrequencyCount(String sourceStr){
        Objects.requireNonNull(sourceStr);
        List<String> list = Stream.of(sourceStr).map(w -> w.split("\\s+")).flatMap(Arrays::stream).collect(Collectors.toList());

        Map<String, Integer> wordCounter = list.stream().collect(Collectors.toMap(w -> w.toLowerCase(), w -> 1, Integer::sum));
        return wordCounter;
    }

    /**
     * 查找参数中长度超过指定长度的单词.只适用于英文
     *
     * @param sourceStr 字符串源
     * @param minLength 指定的长度值
     * @return
     */
    public static String walkWord(String sourceStr, int minLength){
        Objects.requireNonNull(sourceStr);
        List<String> list = Stream.of(sourceStr).map(w -> w.split("\\s+")).flatMap(Arrays::stream).filter(str -> str.length() >= minLength).collect(Collectors.toList());
        if(null==list || list.isEmpty()){
            return null;
        }
        String t = String.join("|", list);
        Random rand = new Random();
        int tmpos=rand.nextInt(list.size());
        String data = list.get(tmpos).toLowerCase();
        return data;
    }

    /**
     * 若str(第一个参数)不可用,则使用defaultStr(第二个参数),反之使用str
     *
     * @param str 目标字符串
     * @param defaultStr 目标不可用时的默认值
     * @return
     */
    public static String optional(String str, String defaultStr){
        Objects.requireNonNull(defaultStr);
        return Optional.ofNullable(str).orElse(defaultStr);
    }

    /**
     * 如果目标为空或null使用默认值
     *
     * @param target 目标可能会为Null的供应函数
     * @param defaultStr 默认的值
     * @return
     */
    public static <T> String optional(Supplier<T> target, String defaultStr){
        try{
            return optional(target.get().toString(), defaultStr);
        }catch(NullPointerException e){
            return defaultStr;
        }
    }

    /**
     * 将json字符串还原为指定类型
     *
     * @param jsonString
     * @return
     */
    public static <T> T jsonToType(String jsonString, TypeToken<T> type){
        Objects.requireNonNull(jsonString);
        return new Gson().fromJson(jsonString, type.getType());
    }

    /**
     * 返回以指定的分隔符连接的字符串
     * 方法使用lang3.StringUtils.join
     *
     * @param elements 字符串集合
     * @param delimiter 分隔符
     * @return
     */
    public static String join(Iterable<? extends CharSequence> elements, String delimiter){
        return String.join(delimiter, elements);
    }

    /**
     * 返回以指定的分隔符连接的字符串
     * 方法使用lang3.StringUtils.join
     *
     * @param elements 字符串集合
     * @param delimiter 分隔符
     * @return
     */
    public static String join(Collection<String> elements, String delimiter){
        if(null == elements || elements.isEmpty()){
            return "";
        }
        Set<String> strs = clearBadEle(elements);
        //两个方法的分隔符所在位置有出入
        //或者:StringUtils.join(records, delimiter);
        return org.apache.commons.lang3.StringUtils.joinWith(delimiter, strs.toArray());
    }

    //汉字转拼音
    //public static String pinyin(String chinaChar){
    //    return null;
    //}

    /**
     * 返回以逗号分隔的字符串
     *
     * @param records 字符串数组
     * @return
     */
    public static String arrToStr(String[] records){
        return Arrays.toString(clearBadEle(Arrays.asList(records)).toArray()).replaceAll("\\[|\\]|\\s", "");
    }

    /**
     * 返回以逗号分隔的字符串
     *
     * @param records 字符串集合
     * @return
     */
    public static String setToStr(Collection<String> records){
        return Arrays.toString(clearBadEle(records).toArray()).replaceAll("\\[|\\]|\\s", "");
    }

    /**
     * 清除掉空,null子字符串
     *
     * @param iterable
     * @return
     */
    private static Set<String> clearBadEle(Iterable<String> iterable){
        Set<String> data = new HashSet<>();
        for(String ele:iterable){
            if(org.apache.commons.lang3.StringUtils.isNotBlank(ele) && !"null".equalsIgnoreCase(ele)){
                data.add(ele.trim());
            }
        }
        return data;
    }

    /**
     * 将驼峰格式的字符串转成：以下划线分隔的字符串
     * 例：camelToSneakCase("topicTop", true); // out: TOPIC_TOP
     * @param str 待转换的字符串
     * @param isUpper 结果是否是全大写的。true全小写;false全小写
     * @return
     */
    public static String camelToSneakCase(String str, boolean isUpper) {
        StringBuilder stringBuilder = new StringBuilder();
        for (char c : str.toCharArray()) {
            char nc = isUpper ? Character.toUpperCase(c) : Character.toLowerCase(c);
            if (Character.isUpperCase(c)) {
                stringBuilder.append('_').append(nc);
            } else {
                stringBuilder.append(nc);
            }
        }
        return stringBuilder.toString();
    }
    /**
     * 将以下划线分隔的字符串转成：驼峰样式
     * 例：sneakToCamelCase("TOPIC_TOP"); // out: topicTop
     * @param str 待转换的字符串
     */
    public static String sneakToCamelCase(String str) {
        if (null == str || str.isEmpty()) {
            return null;
        }
        char SEPARATOR = '_';
        str = str.toLowerCase();
        StringBuilder sb = new StringBuilder(str.length());
        boolean upperCase = false;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);

            if (c == SEPARATOR) {
                upperCase = true;
            } else if (upperCase) {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    //-----HTML 部分
    /**
     * 输出安全的html内容
     *
     * @param htmlStr 包含html标签的字符串
     * @return
     */
    public static String safeHTML(String htmlStr){
        Objects.requireNonNull(htmlStr);
        return Jsoup.clean(htmlStr, Safelist.basic());
    }

    /**
     * 去掉指定的html标签
     *
     * @param htmlStr 包含html标签的字符串
     * @param tagSafeList JSOUP.Safelist
     * @return
     */
    public static String safeHTML(String htmlStr, Safelist tagSafeList){
        Objects.requireNonNull(htmlStr);
        return Jsoup.clean(htmlStr, tagSafeList);
    }

    /**
     * 去掉所有html标签
     *
     * @param htmlStr 包含html标签的字符串
     * @return
     */
    public static String clearHTML(String htmlStr){
        Objects.requireNonNull(htmlStr);
        return Jsoup.clean(htmlStr, Safelist.none());
    }
    /**
     * 用单引号括住集合中的每一个字符串
     *
     * @param params 参数必须是字符串,不适用于数字字符串集合
     * @return 若参数为null或集合是空的返回null
     */
    public static Optional<String> quoteSQLParameters(Collection<String> params) {
        if (null == params || params.isEmpty()) {
            return Optional.empty();
        }
        String data = String.join("','", params);
        return Optional.of(String.format("'%s'", data));
    }
    /**
     * 解析内容中的图片
     *
     * @param htmlStr 包含html标签的字符串
     * @return
     */
    public static List<String> getImgLink(String htmlStr){
        Objects.requireNonNull(htmlStr);
        return getImgLink(htmlStr, Function.identity());
    }

    /**
     * 解析内容中的图片
     *
     * @param htmlStr 包含html标签的字符串
     * @param processFun 找到图片地址时的映射函数
     * @return
     */
    public static List<String> getImgLink(String htmlStr, Function<String,String> processFun){
        Objects.requireNonNull(htmlStr);
        Document document = Jsoup.parseBodyFragment(htmlStr);
        Elements elements = document.select("img[src]");
        List<String> data = new ArrayList<>();
        for (Element e:elements){
            String _link = e.attr("src");
            if(null!=processFun){
                _link = processFun.apply(_link);
            }
            if(isNotBlank(_link)) {
                data.add(_link);
            }
        }
        return data;
    }
    //-----URL
    /**
     *
     * @param url 处理的连接地址
     * @param domain 域名,需要带有http[s]//
     * @param defaultURL 失败时的默认值
     * @return
     */
    public static String getNativeURL(String url, String domain, String defaultURL) {
        if (isNotBlank(url) && url.startsWith(domain)) {
            try {
                URL nu = new URL(url);
                return nu.getFile();
            } catch (MalformedURLException e) {}
        }
        return defaultURL;
    }

    /**
     * Http protocol是否是Https,是返回true
     *
     * @param sitedomain 域名
     * @return 若参数不包含Http protocol信息返回false, 若参数不可用返回false
     */
    public static boolean isHttpsProtocol(String sitedomain) {
        if (!isNotBlank(sitedomain)) {
            return false;
        }
        try {
            String protocol = sitedomain.substring(0, sitedomain.indexOf(":"));
            return protocol.toLowerCase().equals("https");
        } catch (IndexOutOfBoundsException e) {}
        return false;
    }

    /**
     * 使用InetAddress类的hashcode来实现
     *
     * @param ipAddress 若参数不可用返回0
     * @return 若有异常发生会返回-1;
     */
    public static int ipHashcode(String ipAddress){
        if(!isNotBlank(ipAddress)){
            return 0;
        }
        try{
            InetAddress address = InetAddress.getByName(ipAddress);
            return address.hashCode();
        }catch(UnknownHostException e){
            return -1;
        }
    }
    //-----校验 部分
    /**
     * 是否是数字字符串
     * 只适用于正整数,不适用于小数位的字符串,不适用于负数
     * 使用apache common lang3实现
     * Checks if the CharSequence contains only Unicode digits. A decimal point
     * is not a Unicode digit and returns false.
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        if (!isNotBlank(str)) {
            return false;
        }
        return org.apache.commons.lang3.StringUtils.isNumeric(str);
    }

    /**
     * 验证邮箱地址
     *
     * @see ://howtodoinjava.com/regex/java-regex-validate-email-address/
     * @param str
     * @return
     */
    public static boolean isMail(String str) {
        if (!isNotBlank(str)) {
            return false;
        }
        return MAIL_REG.matcher(str).matches();
    }

    /**
     * 是否是字母字符串
     *
     * @param str
     * @return
     */
    public static boolean isAlpha(String str) {
        if (!isNotBlank(str)) {
            return false;
        }
        return org.apache.commons.lang3.StringUtils.isAlpha(str);
    }

    /**
     * 统计目标字符串中指定字符的数量
     * @param target 目标字符串
     * @param charVal 统计的字符
     * @return
     */
    public static long countChar(String target, char charVal){
        return target.chars().filter(ch -> ch == charVal).count();
    }

    public static void main(String[] args) {
        boolean isUsed1 = isNotBlank("null");
        boolean isUsed2 = isNotBlank("");
        boolean isUsed3 = isNotBlank(null);
        boolean isUsed4 = isNotBlank("              ");
        System.out.println("null str="+isUsed1+", empty="+isUsed2+", null ins="+isUsed3+", blank="+isUsed4);
    }
}
